/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.flexengine.server.database;

import com.flexengine.logger.Logger;
import com.flexengine.utils.Reflection;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.TypedQuery;

/**
 * @author Luann R. Athayde
 * @version 1.0
 * @since 1.0
 */
@MappedSuperclass
public abstract class EntityModel implements Serializable, Cloneable {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(insertable = false, updatable = false)
    private int id;
    
    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private Date    creationDate;
    
    @Column
    private boolean enable;
    
    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private Date deleteDate;

    public EntityModel() {
        this.creationDate = new Date();
        this.enable = true;
        this.deleteDate = null;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Date getCreationDate() {
        return creationDate;
    }

    public void setCreationDate(Date creationDate) {
        this.creationDate = creationDate;
    }

    public boolean isEnable() {
        return enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }

    public Date getDeleteDate() {
        return deleteDate;
    }

    public void setDeleteDate(Date deleteDate) {
        this.deleteDate = deleteDate;
    }
    
    public EntityModel find(int id) {
        try {
            EntityManager em = FlexEngineEntityManagerFactory.getDefaultEntityManager();
            if( em!=null && em.isOpen() ) {
                return em.find(getClass(), id);
            }
        } catch(Exception e) {
            Logger.logError("EntityModel::find() - "+e.toString());
        }
        return null;
    }
    
    public <T> EntityModel save() {
        try {
            EntityManager em = FlexEngineEntityManagerFactory.getDefaultEntityManager();
            if( em!=null && em.isOpen() ) {
                em.getTransaction().begin();
                EntityModel eModel = em.merge(this);
                em.flush();
                em.refresh(eModel);
                em.flush();
                em.getTransaction().commit();
                return eModel;
            }
        } catch(Exception e) {
            Logger.logError("EntityModel::save() - "+e.toString());
        }
        return null;
    }
    
    public <T> EntityModel delete() {
        this.setEnable(false);
        this.setDeleteDate(new Date());
        return save();
    }
    
    public <T> EntityModel unDelete() {
        this.setEnable(true);
        this.setDeleteDate(null);
        return save();
    }
    
    public List<EntityModel> list(int first, int maxResults, List<EntityClausule> wheres) {
        return list(first, maxResults, wheres, getClass());
    }
    
    public List<EntityModel> list(int first, int maxResults) {
        return list(first, maxResults, null, getClass());
    }
    
    public List<EntityModel> list(List<EntityClausule> wheres) {
        return list(0, 0, wheres, getClass());
    }
    
    public List<EntityModel> list() {
        return list(0, 0, null, getClass());
    }

    public static List<EntityModel> list(int first, int maxResults, List<EntityClausule> wheres, Class clazz) {
        try {
            EntityManager em = FlexEngineEntityManagerFactory.getDefaultEntityManager();
            if( em!=null && em.isOpen() ) {
                String query = "SELECT e FROM "+clazz.getSimpleName()+" e";
                
                if( wheres!=null && !wheres.isEmpty() ) {
                    query += " WHERE ";
                    for( EntityClausule clausule : wheres ) {
                        Field field = Reflection.getField(clausule.getField(), clazz.newInstance());
                        if( field!=null ) {
                            if( field.getType()==String.class ) {
                                query += "lower(e."+clausule.getField()+") "+clausule.getOperator()+" '"+clausule.getValue().trim().toLowerCase()+"' AND ";
                            } 
                            else if( field.getType()==Date.class ) {
                                query += "lower(to_char(e."+clausule.getField()+", 'dd/MM/yyyy')) "+clausule.getOperator()
                                        +" '"+clausule.getValue().trim().toLowerCase()+"' AND ";
                            }
                            else {
                                query += "e."+clausule.getField()+" "+clausule.getOperator()+" "+clausule.getValue()+" AND ";
                            }
                        }
                    }
                    if( query.endsWith("AND ") ) {
                        query = query.trim().substring(0, query.length()-4);
                    }
                }
                
                Logger.logMessage(clazz.getName()+"::list(): query = "+query);
                
                TypedQuery tQuery = em.createQuery(query, clazz);
                if( first>0 ) {
                    tQuery.setFirstResult(first);
                }
                if( maxResults>0 ) {
                    tQuery.setMaxResults(maxResults);
                }
                return tQuery.getResultList();
            }
        } catch(Exception e) {
            Logger.logError("EntityModel::list() - "+e.toString());
        }
        return new ArrayList<>();
    }
    public static List<EntityModel> list(int first, int maxResults, Class clazz) {
        return list(first, maxResults, null, clazz);
    }
    public static List<EntityModel> list(List<EntityClausule> wheres, Class clazz) {
        return list(0, 0, wheres, clazz);
    }
    public static List<EntityModel> list(Class clazz) {
        return list(0, 0, null, clazz);
    }
    
    @Override
    public Object clone() {
        try {
            Object clone = getClass().newInstance();
            for( Field f : getClass().getDeclaredFields() ) {
                if( !Modifier.isStatic(f.getModifiers()) && !Modifier.isFinal(f.getModifiers()) ) {
                    f.setAccessible(true);
                    f.set(clone, f.get(this));
                }
            }
            return clone;
        } catch(Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 41 * hash + this.id;
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final EntityModel other = (EntityModel) obj;
        if (this.id != other.id) {
            return false;
        }
        return true;
    }
    
    @Override
    public String toString() {
        return getClass().getSimpleName()+"{id="+id+", enable="+enable+"}";
    }
    
    public String hashString() {
        return super.toString();
    }
    
}
